using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace demo.Tool
{
    public class DbHelp : Iado
    {
        public DbHelp(string serverurl)
        {
            connStr = serverurl;
        }
        //private string connStr = "server=.;database=HD2_KQXT_HZL;uid=sa;pwd=123";
        private string connStr = "";
        /// <summary>
        /// 返回受影响行数
        /// </summary>
        /// <param name="sql">传过来的数据库语句</param>
        /// <param name="type">CommandType是SqlCommand对象的一个属性，用于指定执行动作的形式，它告诉.net接下来执行的是一个文本（text）、存储过程（）、还是表名称（table）</param>
        /// <param name="pars">传过来的SqlParameter[]类型的参数，SqlParameter[]是用来给数据库定义的变量传值用的</param>
        /// <returns></returns>
        public int ExecuteNonquery(string sql, CommandType type, params SqlParameter[] pars) //params表示可变参数，也就是说pars可有可无
        {
            try
            {
                operateLog.WriteLog("执行数据库非查询操作开始");
                operateLog.WriteLog("SQL语句：" + sql);
                operateLog.WriteLog("连接字符串：" + (string.IsNullOrEmpty(connStr) ? "空" : "已设置"));
                
                using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
                {
                    using (SqlCommand cmd = new SqlCommand(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
                    {
                        if (pars != null)
                        {
                            cmd.Parameters.AddRange(pars);  //添加pars参数数组
                            operateLog.WriteLog("参数数量：" + pars.Length);
                        }
                        else
                        {
                            operateLog.WriteLog("无参数");
                        }
                        
                        cmd.CommandType = type; //把CommandType配置给apter
                        cmd.CommandTimeout = 30; // 设置命令超时时间为30秒
                        
                        operateLog.WriteLog("开始打开数据库连接");
                        conn.Open();    //打开数据库
                        operateLog.WriteLog("数据库连接已打开");
                        
                        int result = cmd.ExecuteNonQuery();
                        operateLog.WriteLog("数据库非查询操作完成，受影响行数：" + result);
                        return result;
                    }
                }
            }
            catch (SqlException sqlEx)
            {
                operateLog.WriteLog("数据库SQL异常：" + sqlEx.Message);
                operateLog.WriteLog("SQL错误号：" + sqlEx.Number);
                operateLog.WriteLog("SQL错误状态：" + sqlEx.State);
                operateLog.WriteLog("SQL语句：" + sql);
                throw new Exception($"数据库操作失败：{sqlEx.Message} (错误号：{sqlEx.Number})", sqlEx);
            }
            catch (InvalidOperationException ioEx)
            {
                operateLog.WriteLog("数据库连接操作异常：" + ioEx.Message);
                operateLog.WriteLog("连接字符串：" + connStr);
                throw new Exception($"数据库连接失败：{ioEx.Message}", ioEx);
            }
            catch (Exception ex)
            {
                operateLog.WriteLog("数据库操作发生未知异常：" + ex.Message);
                operateLog.WriteLog("异常类型：" + ex.GetType().Name);
                operateLog.WriteLog("异常堆栈：" + ex.StackTrace);
                throw new Exception($"数据库操作异常：{ex.Message}", ex);
            }
        }
        /// <summary>
        /// 返回结果集的第一行第一列
        /// </summary>
        /// <param name="sql">传过来的数据库语句</param>
        /// <param name="type">CommandType是SqlCommand对象的一个属性，用于指定执行动作的形式，它告诉.net接下来执行的是一个文本（text）、存储过程（）、还是表名称（table）</param>
        /// <param name="pars">传过来的SqlParameter[]类型的参数，SqlParameter[]是用来给数据库定义的变量传值用的</param>
        /// <returns></returns>
        public object ExecuteScalar(string sql, CommandType type, params SqlParameter[] pars) //params表示可变参数，也就是说pars可有可无
        {
            try
            {
                operateLog.WriteLog("执行数据库标量查询操作开始");
                operateLog.WriteLog("SQL语句：" + sql);
                
                using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
                {
                    using (SqlCommand cmd = new SqlCommand(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
                    {
                        if (pars != null)
                        {
                            cmd.Parameters.AddRange(pars);  //添加pars参数数组
                            operateLog.WriteLog("参数数量：" + pars.Length);
                        }
                        else
                        {
                            operateLog.WriteLog("无参数");
                        }
                        
                        cmd.CommandType = type; //把CommandType配置给apter
                        cmd.CommandTimeout = 30; // 设置命令超时时间为30秒
                        
                        operateLog.WriteLog("开始打开数据库连接");
                        conn.Open();    //打开数据库
                        operateLog.WriteLog("数据库连接已打开");
                        
                        object result = cmd.ExecuteScalar();
                        operateLog.WriteLog("数据库标量查询操作完成，结果：" + (result?.ToString() ?? "NULL"));
                        return result;
                    }
                }
            }
            catch (SqlException sqlEx)
            {
                operateLog.WriteLog("数据库SQL异常：" + sqlEx.Message);
                operateLog.WriteLog("SQL错误号：" + sqlEx.Number);
                operateLog.WriteLog("SQL语句：" + sql);
                throw new Exception($"数据库查询失败：{sqlEx.Message} (错误号：{sqlEx.Number})", sqlEx);
            }
            catch (InvalidOperationException ioEx)
            {
                operateLog.WriteLog("数据库连接操作异常：" + ioEx.Message);
                throw new Exception($"数据库连接失败：{ioEx.Message}", ioEx);
            }
            catch (Exception ex)
            {
                operateLog.WriteLog("数据库标量查询发生未知异常：" + ex.Message);
                operateLog.WriteLog("异常类型：" + ex.GetType().Name);
                throw new Exception($"数据库查询异常：{ex.Message}", ex);
            }
        }
        /// <summary>
        /// 调用存储过程
        /// </summary>
        /// <param name="dbo">存储过程名字</param>
        /// <param name="data">键值对集合（存储过程的参数）</param>
        /// <returns>datatable类型</returns>
        public DataTable Ex_Procedure(string dbo, Dictionary<string, string> data)
        {
            DataTable table = new DataTable();
            using (SqlConnection sqlconn = new SqlConnection(connStr))
            {
                sqlconn.Open();
                if (sqlconn.State == ConnectionState.Open)
                {
                    using (SqlCommand cmd = new SqlCommand(dbo, sqlconn))
                    {
                        if (data.Count != 0)
                        {
                            foreach (var item in data)
                            {
                                cmd.Parameters.AddWithValue(item.Key, item.Value);
                            }
                        }
                        cmd.CommandType = CommandType.StoredProcedure;
                        SqlDataAdapter sda = new SqlDataAdapter(cmd);
                        DataSet ds = new DataSet();
                        sda.Fill(ds);
                        table = ds.Tables[0];
                    }
                }
                sqlconn.Close();
            }
            return table;
        }
        /// <summary>
        /// 调用存储过程
        /// </summary>
        /// <param name="dbo">存储过程名字</param>
        /// <param name="data">键值对集合（存储过程的参数）</param>
        /// <returns>返回一个List集合</returns>
        public List<T> Ex_ProcedureList<T>(string dbo, Dictionary<string, object> data) where T : class, new()
        {
            DataTable table = new DataTable();
            using (SqlConnection sqlconn = new SqlConnection(connStr))
            {
                sqlconn.Open();
                if (sqlconn.State == ConnectionState.Open)
                {
                    using (SqlCommand cmd = new SqlCommand(dbo, sqlconn))
                    {
                        if (data != null && data.Count != 0)
                        {
                            foreach (var item in data)
                            {
                                cmd.Parameters.AddWithValue(item.Key, item.Value);
                            }
                        }
                        cmd.CommandType = CommandType.StoredProcedure;
                        SqlDataAdapter sda = new SqlDataAdapter(cmd);
                        DataSet ds = new DataSet();
                        sda.Fill(ds);
                        table = ds.Tables[0];
                    }
                }
                sqlconn.Close();
            }
            return DataTableToEntity<T>.TableToList(table);
        }
        /// <summary>
        /// 调用存储过程获取数据
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public List<T> GetDataList<T>(string sql, object obj) where T : class, new()
        {
            DataTable dt = new DataTable(); //创建一张datatable
            using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
            {
                conn.Open();
                using (SqlDataAdapter apter = new SqlDataAdapter(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
                {
                    if (obj != null)
                    {
                        var parame = ToSqlParameterArray(obj);
                        apter.SelectCommand.Parameters.AddRange(parame);  //添加pars参数数组
                    }
                    apter.SelectCommand.CommandType = CommandType.Text; //把CommandType配置给apter
                    apter.Fill(dt); //把查到的数据填充到dt中
                }
                conn.Close();
            }
            if (dt.Rows.Count == 0)
            {
                return null;
            }
            return DataTableToEntity<T>.TableToList(dt);  //返回dt
        }
        /// <summary>
        ///调用sql语句获取数据
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="obj">参数</param>
        /// <returns></returns>
        public T GetDataT<T>(string sql, object obj) where T : class, new()
        {
            DataTable dt = new DataTable(); //创建一张datatable
            using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
            {
                conn.Open();
                using (SqlDataAdapter apter = new SqlDataAdapter(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
                {
                    if (obj != null)
                    {
                        var parame = ToSqlParameterArray(obj);
                        apter.SelectCommand.Parameters.AddRange(parame);  //添加pars参数数组
                    }
                    apter.SelectCommand.CommandType = CommandType.Text; //把CommandType配置给apter
                    apter.Fill(dt); //把查到的数据填充到dt中
                }
                conn.Close();
            }
            if (dt.Rows.Count == 0)
            {
                return null;
            }
            return DataTableToEntity<T>.TableToEntity(dt);  //返回dt
        }
        /// <summary>
        /// 返回受影响行数
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="obj">sql用到的语句</param>
        /// <returns></returns>
        public int ExecuteNonquery(string sql, object obj)
        {
            int count = 0;
            using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
            {
                using (SqlDataAdapter cmd = new SqlDataAdapter(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
                {
                    if (obj != null)
                    {
                        var parame = ToSqlParameterArray(obj);
                        cmd.SelectCommand.Parameters.AddRange(parame);  //添加pars参数数组
                    }
                    cmd.SelectCommand.CommandType = CommandType.Text; //把CommandType配置给apter
                    conn.Open();    //打开数据库
                    count = cmd.SelectCommand.ExecuteNonQuery();
                    conn.Close();
                }
            }
            return count;
        }
        /// <summary>
        /// 获取datatable
        /// </summary>
        /// <param name="sql">传过来的数据库语句</param>
        /// <param name="obj">参数</param>
        /// <returns></returns>
        public DataTable GetDataTable(string sql, object obj)
        {
            CommandType type = CommandType.Text;
            using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
            {
                using (SqlDataAdapter apter = new SqlDataAdapter(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
                {
                    if (obj != null)
                    {
                        SqlParameter[] pars = ToSqlParameterArray(obj);
                        apter.SelectCommand.Parameters.AddRange(pars);  //添加pars参数数组
                    }
                    apter.SelectCommand.CommandType = type; //把CommandType配置给apter
                    DataTable dt = new DataTable(); //创建一张datatable
                    apter.Fill(dt); //把查到的数据填充到dt中
                    return dt;  //返回dt
                }
            }
        }


        /// <summary>
        /// 返回json字符(多数据)
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        //public string GetDataJsonList(string sql, object obj)
        //{
        //    CommandType type = CommandType.Text;
        //    using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
        //    {
        //        using (SqlDataAdapter apter = new SqlDataAdapter(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
        //        {
        //            if (obj != null)
        //            {
        //                SqlParameter[] pars = ToSqlParameterArray(obj);
        //                apter.SelectCommand.Parameters.AddRange(pars);  //添加pars参数数组
        //            }
        //            apter.SelectCommand.CommandType = type; //把CommandType配置给apter
        //            DataTable dt = new DataTable(); //创建一张datatable
        //            apter.Fill(dt); //把查到的数据填充到dt中
        //            return ToJson.TableToJsonList(dt);  //返回dt
        //        }
        //    }
        //}
        ///// <summary>
        ///// 返回json字符(单数据)
        ///// </summary>
        ///// <param name="sql"></param>
        ///// <param name="obj"></param>
        ///// <returns></returns>
        //public string GetDataJson(string sql, object obj)
        //{
        //    CommandType type = CommandType.Text;
        //    using (SqlConnection conn = new SqlConnection(connStr))  //using(){}作为语句，用于定义一个范围，在此范围的末尾将释放对象，GC垃圾回收机制
        //    {
        //        using (SqlDataAdapter apter = new SqlDataAdapter(sql, conn))    //SqlDataAdapter是Dataset和SQL Server之间的桥接器，可以用于检索和保存数据
        //        {
        //            if (obj != null)
        //            {
        //                SqlParameter[] pars = ToSqlParameterArray(obj);
        //                apter.SelectCommand.Parameters.AddRange(pars);  //添加pars参数数组
        //            }
        //            apter.SelectCommand.CommandType = type; //把CommandType配置给apter
        //            DataTable dt = new DataTable(); //创建一张datatable
        //            apter.Fill(dt); //把查到的数据填充到dt中
        //            return ToJson.TableToJson(dt);  //返回dt
        //        }
        //    }
        //}

        /// <summary>
        /// object转为SqlParameter
        /// </summary>
        /// <param name="obj">参数</param>
        /// <returns></returns>
        private static SqlParameter[] ToSqlParameterArray(object obj)
        {
            List<SqlParameter> parms = new List<SqlParameter>();
            PropertyInfo[] propertys = obj.GetType().GetProperties();
            foreach (PropertyInfo pi in propertys)
            {
                // 判断此属性是否有Getter
                if (!pi.CanRead)
                {
                    continue;
                }
                //object value = pi.GetValue(obj);
                //parms.Add(new SqlParameter("@" + pi.Name.ToLower(), value));
            }
            return parms.ToArray();
        }
        /// <summary>
        /// 把整个table的数据添加到数据库里（table字段要和数据库里表的字段一样）
        /// </summary>
        /// <param name="table">添加的tables数据源</param>
        /// <param name="ServerTableName">数据库表名</param>
        /// <returns></returns>
        public bool AddDataTableToDB(DataTable table, string ServerTableName)
        {
            SqlTransaction tran = null;//声明一个事务对象
            try
            {
                using (SqlConnection conn = new SqlConnection(connStr))
                {
                    conn.Open();//打开链接
                    using (tran = conn.BeginTransaction())
                    {
                        using (SqlBulkCopy copy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, tran))
                        {
                            copy.DestinationTableName = ServerTableName;    //info待传入数据的数据表名称--info表结构需同c#中定义的结构一致
                            copy.WriteToServer(table);                      //执行把DataTable中的数据写入DB
                            tran.Commit();                                //提交事务
                            return true;               //返回True 执行成功！
                        }
                    }
                }
            }
            catch (Exception)
            {
                if (null != tran)
                    tran.Rollback();
                //MessageBox.Show(ex.Message);
                //return false;//返回False 执行失败！
                return false;
            }
        }
        /// <summary>
        /// 调用存储过程
        /// </summary>
        /// <param name="dbo">存储过程名字</param>
        /// <param name="data">键值对集合（存储过程的参数）</param>
        /// <returns>返回一个List集合</returns>
        public List<T> Ex_ProcedureData<T>(string dbo, Dictionary<string, object> data) where T : class, new()
        {
            DataTable table = new DataTable();
            using (SqlConnection sqlconn = new SqlConnection(connStr))
            {
                sqlconn.Open();
                if (sqlconn.State == ConnectionState.Open)
                {
                    using (SqlCommand cmd = new SqlCommand(dbo, sqlconn))
                    {
                        if (data.Count != 0)
                        {
                            foreach (var item in data)
                            {
                                cmd.Parameters.AddWithValue(item.Key, item.Value);
                            }
                        }
                        cmd.CommandType = CommandType.StoredProcedure;
                        SqlDataAdapter sda = new SqlDataAdapter(cmd);
                        DataSet ds = new DataSet();
                        sda.Fill(ds);
                        table = ds.Tables[0];
                    }
                }
                sqlconn.Close();
            }
            return DataTableToEntity<T>.TableToList(table);
        }
    }
}
